//============================================================================================
/**
 * @file	push_ai.c
 * @brief	おしくらまんじゅうAI
 * @author	Nozomu Saito
 *
 * モジュール名：PushAI_
 */
//============================================================================================

#include "push_ai.h"
#include "point_ring.h"

#define POKE_NUM_MAX	(ENTRY_MAX*ENTRY_MEMBER_NUM_MAX)
#define CELL_SIZE	(32)
#define CELL_OFS	(16)		//場外幅16
#define SIDE_CELL_NUM	(5)
#define AROUND_CELL_MAX	(16)
#define NEIGHBORING_CELL_MAX	(9)
#define ALL_CELL_NUM	(AROUND_CELL_MAX+NEIGHBORING_CELL_MAX)
#define CELL_CENTER_IDX	(4)
#define ALL_CELL_CENTER_IDX	(12)
#define CELL_WIDTH_NUM	(7)
#define CELL_HEIGHT_NUM	(5)
#define CELL_IDX_MAX	((CELL_WIDTH_NUM*CELL_HEIGHT_NUM)-1)
#define GRADING_OK_MINIMUM	(3)		//評価の結果移動先と認められる最低評価値
#define EXE_FRAME_ALONE	(20)			//非通信時AI実行フレーム間隔
#define EXE_FRAME_COMM	(10)			//通信時AI実行フレーム間隔
#define NONE_GRADING	(-100)		//非評価点。適当にすごく大きな値（評価で算出されない値）

//#define DEBUG_AI_PRINT

typedef struct POKE_INFO_tag
{
	int CellIdx;	//-1で気絶してることにする
	int Power;
	const u16 *Stamina;
	const VecFx32 *Pos;
	const POKE_STATE *State;
	int PokeIndex;
	VecFx32 *DstPos;
	POKE_ACT_PTR ActPtr;
}AI_POKE_INFO;

typedef struct CELL_INFO_tag
{
	int CX;
	int CY;
	int X;
	int Y;
	BOOL Valid;
	int Index;
	int Grading;
	int OrgIdx;
}CELL_INFO;

typedef  struct INFO_NODE_tag INFO_NODE;

typedef struct INFO_NODE_tag
{
	INFO_NODE *Prev;
	INFO_NODE *Next;
	CELL_INFO *Info;
}INFO_NODE;

typedef struct PUSH_AI_CONT_tag
{
	AI_POKE_INFO Info[POKE_NUM_MAX];
	CELL_INFO Cell[ALL_CELL_NUM];
	int RingCenterCell[RING_MAX];
	BOOL Execute;
	BOOL NoneExeCount[ENTRY_MEMBER_NUM_MAX-1];
	VecFx32 OldPoint[ENTRY_MEMBER_NUM_MAX-1];
	u8 NpcNum;	//0〜3
	u8 ExeNpcIdx;	//0〜2
	u8 Frame;
	u8 CntFrame;
	u8 ExePokeIdx;	//0〜2
	u8 NpcStartIdx;	//1〜3
	u8 PlayerNum;	//1〜4
	u8 dummy;
}PUSH_AI_CONT;

static void ExecuteAI(	PUSH_AI_CONT_PTR ptr,
						const u8 inTarget,
						const u8 inFldType );
static void SetMovePoint(const u8 inNum, CELL_INFO **outInfo);
static void CalcGrading(PUSH_AI_CONT_PTR ptr,
						const u8 inNum, const RING_PRM *inRingPrm,
						const AI_POKE_INFO *inTarget, CELL_INFO **outInfo);
static int GetCellIdx(const int inX, const int inY);
static int CheckGrading(PUSH_AI_CONT_PTR ptr, const RING_PRM *inRingPrm, const int inCellIdx,
		const AI_POKE_INFO *inInfo, const int inX,  const int inY, BOOL *outIsCenter);
static int CalcGradingPower(PUSH_AI_CONT_PTR ptr, const AI_POKE_INFO *inInfo, const int inCellIdx);
static BOOL CheckRingIn(const RING_PRM *inRingPrm, const fx32 inX, const fx32 inY);
static BOOL CheckCircleCenter(PUSH_AI_CONT_PTR ptr, const u8 inCellIdx);
static void GetCircleCenter(PUSH_AI_CONT_PTR ptr, const RING_PRM *inRingPrm, const u8 inCellIdx,
							int *outX, int *outY);
static BOOL GetCell(const u16 inX, const u16 inY, s16 *outCX, s16 *outCY);
static u8 SortInfo(const u8 inSortNum, CELL_INFO **inInfo, CELL_INFO **outInfo);

//----------------------------------------------------------------------------
/**
 *	@brief	初期化
 *
 *	@param	inHeapID		ヒープID
 *	@param	inNPCNum		NPCの数
 *	@param	inType			ゲームタイプ（通信してるか）
 *
 *	@return ptr				コントロールポインタ
 */
//-----------------------------------------------------------------------------
PUSH_AI_CONT_PTR PushAI_Init(const int inHeapID, const int inNpcNum, const PKTHLN_GAME_TYPE inType)
{
	PUSH_AI_CONT_PTR cnt_ptr;
	//メモリアロケーション
	int size = sizeof(PUSH_AI_CONT);
	cnt_ptr = sys_AllocMemory(inHeapID, size);
	MI_CpuClear8( cnt_ptr, size);

	cnt_ptr->NpcNum = inNpcNum;
	cnt_ptr->PlayerNum = ENTRY_MEMBER_NUM_MAX-inNpcNum;
	cnt_ptr->NpcStartIdx = cnt_ptr->PlayerNum;
	if (inType == PKTHLN_GAME_ALONE){
		cnt_ptr->CntFrame = EXE_FRAME_ALONE;
	}else{
		cnt_ptr->CntFrame = EXE_FRAME_COMM;
	}
	cnt_ptr->Frame = cnt_ptr->CntFrame;	//すぐに起動するようにしておく
	return cnt_ptr;
}

//----------------------------------------------------------------------------
/**
 *	@brief	AIセットアップ
 *
 *	@param	ptr				コントロールポインタ
 *	@param	inIdx			管理インデックス
 *	@param	inPower			ポケモンのパワー
 *	@param	*inPos			ポケモンの現在地
 *	@param	*inState		ポケモンのステート
 *	@param	dstPos			ポケモンの目的地をさす座標へのポインタ
 *	@param	actptr			ポケモンアクターポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void PushAI_Setup(PUSH_AI_CONT_PTR ptr, const u8 inIdx,
		const int inPower, const u16 *inStamina, const VecFx32 *inPos,
		const POKE_STATE *inState, VecFx32 *dstPos, POKE_ACT_PTR actPtr)
{
	ptr->Info[inIdx].Power = inPower;
	ptr->Info[inIdx].Stamina = inStamina;
	ptr->Info[inIdx].Pos = inPos;
	ptr->Info[inIdx].State = inState;
	ptr->Info[inIdx].CellIdx = -1;
	ptr->Info[inIdx].PokeIndex = inIdx;
	ptr->Info[inIdx].ActPtr = actPtr;
	//プレーヤーの最終目的地は決して書き換えられないように、そもそもセットしないようにしておく
	if ( (inIdx/ENTRY_MAX) >= ptr->NpcStartIdx ){
		ptr->Info[inIdx].DstPos = dstPos;
	}else{
		ptr->Info[inIdx].DstPos = NULL;
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	解放
 *
 *	@param	ptr				コントロールポインタ
 *
 *	@return none
 */
//-----------------------------------------------------------------------------
void PushAI_End(PUSH_AI_CONT_PTR ptr)
{
	//メモリ解放
	sys_FreeMemoryEz(ptr);
}



//----------------------------------------------------------------------------
/**
 *	@brief	ＡＩ実行
 *
 *	@param	ptr			管理ポインタ
 *	@param	inFldType	フィールドタイプ
 *
 *	@return u8		動かしたポケインデックス
 */
//-----------------------------------------------------------------------------
u8 PushAI_Execute(PUSH_AI_CONT_PTR ptr, const u8 inFldType)
{
	u8 exe_idx;
	exe_idx = POKE_NUM_MAX;

	if (ptr->PlayerNum == ENTRY_MEMBER_NUM_MAX){
		//AIなし
		return exe_idx;
	}
	ptr->Frame++;

	if (ptr->Frame >= ptr->CntFrame){
		u8 i;
		//実行状態にする
		ptr->Execute = TRUE;
		ptr->Frame = 0;
		//動作見送り制御カウンタクリア
		for(i=0;i<ptr->NpcNum;i++){
			ptr->NoneExeCount[i] = 0;
		}
	}

	if (ptr->Execute){	//実行状態
		int exe_poke;
		//実行NPCのポケ決定
		exe_poke = ((ptr->NpcStartIdx + ptr->ExeNpcIdx) * ENTRY_MAX) + (ENTRY_MAX - 1 - ptr->ExePokeIdx);
		GF_ASSERT(exe_poke/ENTRY_MAX >= ptr->NpcStartIdx );
		//実行
		ExecuteAI(ptr, exe_poke, inFldType);
		exe_idx = exe_poke;
		//実行NPCのシフト
		ptr->ExeNpcIdx++;
#if 1
		if (ptr->ExeNpcIdx >= ptr->NpcNum){	//シフト後一巡したら、
			ptr->ExeNpcIdx = 0;
			//実行するポケモンインデックスを変更する
			ptr->ExePokeIdx = (ptr->ExePokeIdx+1) % ENTRY_MAX;
			//実行フラグを落とす
			ptr->Execute = FALSE;
		}
#else
		if (ptr->ExeNpcIdx >= ptr->NpcNum){
			ptr->ExeNpcIdx = 0;
			//実行フラグを落とす
			ptr->Execute = FALSE;
		}
#endif
	}
	return exe_idx;
}

//----------------------------------------------------------------------------
/**
 *	@brief		AI実行
 *
 *	@param	ptr				コントローラポインタ
 *	@param	inTarget		対象ポケインデックス
 *	@param	inFldType		フィールドタイプ
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void ExecuteAI(PUSH_AI_CONT_PTR ptr, const u8  inTarget, const u8 inFldType)
{
	int i;
	int aro_count,nei_count;
	u8 num;
	s16 cx,cy;
	int sx,sy;
	RING_PRM ring_prm[RING_MAX];

	CELL_INFO *all_cel[ALL_CELL_NUM];
	CELL_INFO *nei_cel[NEIGHBORING_CELL_MAX];
	CELL_INFO *aro_cel[AROUND_CELL_MAX];
	CELL_INFO *sort_work[ALL_CELL_NUM];
	AI_POKE_INFO *target_info;

	target_info = &ptr->Info[inTarget];

	if ( *target_info->State == POKE_NONE ){
		return;
	}

	if ( target_info->DstPos == NULL ){
		GF_ASSERT(0);
		return;
	}

	//１ループする間に必ず１匹は行動するようにする
	if (ptr->NoneExeCount[ptr->ExePokeIdx] < ENTRY_MAX){
		//行動する？
		if(gf_rand()%100<30){	//行動しない
			ptr->NoneExeCount[ptr->ExePokeIdx]++;
			return;
		}
	}

	//全ポケのセル位置情報を作成
	for(i=0;i<POKE_NUM_MAX;i++){
		AI_POKE_INFO *info = &ptr->Info[i];
		if (*(info->State) != POKE_NONE){
			int x,y;
			fx32 fx,fy;
			int r;
			PokeAct_GetHitData(info->ActPtr, info->Pos->x, info->Pos->y, &fx, &fy, &r);
			x = fx / FX32_ONE;
			y = fy / FX32_ONE;
			//セルインデックス取得
			info->CellIdx = GetCellIdx(x, y);
#ifdef DEBUG_AI_PRINT
			OS_Printf("poke_cel_idx:%d\n",info->CellIdx);
#endif
		}else{
			info->CellIdx = -1;
		}
	}
	//自分は影の位置で取る
	{
		PokeAct_GetShadowPosByTouchPos(target_info->ActPtr,
				target_info->Pos->x / FX32_ONE, target_info->Pos->y / FX32_ONE,
				&sx, &sy);
		target_info->CellIdx = GetCellIdx(sx, sy);
#ifdef DEBUG_AI_PRINT
		OS_Printf("my_poke_cel_idx:%d\n",target_info->CellIdx);
#endif
	}

	//フィールドのリング情報を取得
	PtRing_GetRingData(inFldType, ring_prm);
	//円の中心があるセル（最大4）を算出
	for(i=0;i<RING_MAX;i++){
#ifdef DEBUG_AI_PRINT
		OS_Printf("CirCle:Ran_Cent = %d, %d,%d\n",ring_prm[i].Range, ring_prm[i].X, ring_prm[i].Y);
#endif
		if (ring_prm[i].Range > 0 ){
			ptr->RingCenterCell[i] = GetCellIdx(ring_prm[i].X, ring_prm[i].Y);
		}else{
			ptr->RingCenterCell[i] = -1;
		}
	}

	//自分のいるセル座標を取得
	{
		BOOL rc;
		rc = GetCell(sx, sy, &cx, &cy);
		GF_ASSERT(rc);
	}

	nei_count = 0;
	aro_count = 0;
	//現在セル情報から、自分の周囲セルの座標を取得
	for (i=0;i<ALL_CELL_NUM;i++){
		int ofs_x, ofs_y;
		ptr->Cell[i].OrgIdx = i;
		ptr->Cell[i].CX = (i%SIDE_CELL_NUM)-2 + cx;
		ptr->Cell[i].CY = (i/SIDE_CELL_NUM)-2 + cy;

		if ((ptr->Cell[i].CX < 0) || (ptr->Cell[i].CY < 0) ||
				(CELL_WIDTH_NUM<=ptr->Cell[i].CX) || (CELL_HEIGHT_NUM<=ptr->Cell[i].CY) ){
			ptr->Cell[i].Valid = FALSE;		//場外
		}else{
			ptr->Cell[i].Valid = TRUE;
		}
		//自分のいる中心セルと隣接8セル計9セルとさらに外周16セルを分ける
		ofs_x = ptr->Cell[i].CX - cx;
		ofs_y = ptr->Cell[i].CY - cy;

		all_cel[i] = &ptr->Cell[i];
		if ( ((-1<=ofs_x) && (ofs_x<=1))&&
			 ((-1<=ofs_y) && (ofs_y<=1)) ){
			//隣接8　OR　中心セル
			nei_cel[nei_count++] = &ptr->Cell[i];
		}else{
			//外周
			aro_cel[aro_count++] = &ptr->Cell[i];
		}
	}

	GF_ASSERT(nei_count == NEIGHBORING_CELL_MAX);
	GF_ASSERT(aro_count == AROUND_CELL_MAX);

#ifdef DEBUG_AI_PRINT
	OS_Printf("mypos::%d,%d\n",cx,cy);
	for(i=0;i<ALL_CELL_NUM;i++){
		OS_Printf("CELL_XY::%d,%d\n",ptr->Cell[i].CX, ptr->Cell[i].CY);
	}
	for(i=0;i<NEIGHBORING_CELL_MAX;i++){
		OS_Printf("NEI_CELL_XY::%d,%d\n",nei_cel[i]->CX, nei_cel[i]->CY);
	}
	for(i=0;i<AROUND_CELL_MAX;i++){
		OS_Printf("ARO_CELL_XY::%d,%d\n",aro_cel[i]->CX, aro_cel[i]->CY);
	}
#endif

	//周囲8箇所の座標を抽選
	SetMovePoint(NEIGHBORING_CELL_MAX, nei_cel);
	//自分のいるセルは自分の座標で上書き
	nei_cel[CELL_CENTER_IDX]->X = sx;
	nei_cel[CELL_CENTER_IDX]->Y = sy;

	//決定した場所の評価をする
	CalcGrading(ptr, NEIGHBORING_CELL_MAX, ring_prm, target_info, nei_cel );

#ifdef DEBUG_AI_PRINT
	for(i=0;i<NEIGHBORING_CELL_MAX;i++){
		OS_Printf("NEI_GRADING::%d\n",nei_cel[i]->Grading);
	}
#endif

	//旧ポイントの評価
	;

	//評価点の高い順にソート
	num = SortInfo(NEIGHBORING_CELL_MAX, nei_cel, sort_work);
#ifdef DEBUG_AI_PRINT
	for(i=0;i<NEIGHBORING_CELL_MAX;i++){
		OS_Printf("NEI_SORT_GRADING::%d\n",sort_work[i]->Grading);
	}
	OS_Printf("NUM::%d\n",num);
#endif
	//一番高い評価点は3以上？
	if (sort_work[0]->Grading>=GRADING_OK_MINIMUM){
		u8 idx;
		int x,y;
		//一番高い評価点のセルの中からランダムでひとつ選ぶ
		idx = gf_rand()%num;
		PokeAct_GetTouchPosByShadowPos(target_info->ActPtr,
									sort_work[idx]->X, sort_work[idx]->Y, &x, &y);
		target_info->DstPos->x = x * FX32_ONE;
		target_info->DstPos->y = y * FX32_ONE;
		target_info->DstPos->z = 0;
	}else{ //再抽選
		u8 idx;
		int x,y;
		//外周の座標抽選
		SetMovePoint(AROUND_CELL_MAX, aro_cel);
/**
		//決定した場所の評価をする
		CalcGrading(ptr, AROUND_CELL_MAX, ring_prm, target_info, aro_cel );
#ifdef DEBUG_AI_PRINT
		for(i=0;i<AROUND_CELL_MAX;i++){
			OS_Printf("ARO_GRADING::%d\n",aro_cel[i]->Grading);
		}
#endif
		//評価点の高い順にソート
		num = SortInfo(AROUND_CELL_MAX, aro_cel, sort_work);

#ifdef DEBUG_AI_PRINT
		for(i=0;i<AROUND_CELL_MAX;i++){
			OS_Printf("ARO_SORT_GRADING::%d\n",sort_work[i]->Grading);
		}
		OS_Printf("NUM::%d\n",num);
#endif
*/
		CalcGrading(ptr, ALL_CELL_NUM, ring_prm, target_info, all_cel );
		num = SortInfo(ALL_CELL_NUM, all_cel, sort_work);

		//一番高い評価点のセルの中からランダムでひとつ選ぶ
		idx = gf_rand()%num;
		PokeAct_GetTouchPosByShadowPos(target_info->ActPtr,
									sort_work[idx]->X, sort_work[idx]->Y, &x, &y);
		target_info->DstPos->x = x * FX32_ONE;
		target_info->DstPos->y = y * FX32_ONE;
		target_info->DstPos->z = 0;
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief		移動先候補を決定
 *
 *	@param	inNum		セル数
 *	@param	outInfo		バッファ
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void SetMovePoint(const u8 inNum, CELL_INFO **outInfo)
{
	u8 i;
	for(i=0;i<inNum;i++){
		if (outInfo[i]->Valid){
			outInfo[i]->X = CELL_OFS + (outInfo[i]->CX*CELL_SIZE) + (gf_rand()%CELL_SIZE);
			outInfo[i]->Y = CELL_OFS + (outInfo[i]->CY*CELL_SIZE) + (gf_rand()%CELL_SIZE);
			outInfo[i]->Index = outInfo[i]->CY * CELL_WIDTH_NUM + outInfo[i]->CX;
		}else{
			outInfo[i]->X = -1;
			outInfo[i]->Y = -1;
			outInfo[i]->Index = -1;
		}
#ifdef DEBUG_AI_PRINT
		OS_Printf("抽選XY=%d,%d\n",outInfo[i]->X,outInfo[i]->Y);
#endif
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief		評点を計算
 *
 *  @param	ptr			管理ポインタ
 *	@param	inNum		計算セル数
 *	@param	inRingPrm	リングパラメータポインタ
 *	@param	inTarget	対象ポケ
 *	@param	outInfo		バッファ
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void CalcGrading(PUSH_AI_CONT_PTR ptr,
						const u8 inNum, const RING_PRM *inRingPrm,
						const AI_POKE_INFO *inTarget, CELL_INFO **outInfo)
{
	u8 i;
	//決定した場所の評価をする
	for(i=0;i<inNum;i++){
		BOOL is_center;
		//無効セルははじく
		if (!outInfo[i]->Valid){
			outInfo[i]->Grading = NONE_GRADING;
			continue;
		}
		outInfo[i]->Grading = CheckGrading(ptr, inRingPrm, outInfo[i]->Index,
				inTarget, outInfo[i]->X, outInfo[i]->Y, &is_center);
		if (is_center){
			//中心座標を取得し、目的位置を変更する
			GetCircleCenter(ptr, inRingPrm, outInfo[i]->Index,
							&(outInfo[i]->X), &(outInfo[i]->Y) );
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief		指定座標のセルインデックスを取得
 *
 *	@param	inX		指定X座標
 *	@param	inY		指定Y座標
 *
 *	@return	int セルインデックス
 */
//-----------------------------------------------------------------------------
static int GetCellIdx(const int inX, const int inY)
{
	int idx;
	BOOL rc;
	int cx,cy;
	cx = (inX - CELL_OFS) / CELL_SIZE;
	cy = (inY - CELL_OFS) / CELL_SIZE;
	if ((cx < 0) || (cy < 0)){
		return -1;
	}

	idx = cy * CELL_WIDTH_NUM + cx;

	if (idx > CELL_IDX_MAX){
		idx = -1;
	}

	return idx;
}

//----------------------------------------------------------------------------
/**
 *	@brief	評価点の計算
 *
 *	@param	ptr				管理ポインタ
 *	@param	*inRingPrm		リングパラメータ
 *	@param	inCellIdx		セルインデックス
 *	@param	*inInfo			対象ポケ
 *	@param	inX				対象X座標
 *	@param	inY				対象Y座標
 *	@param outIsCenter		リング中心判定で、評価されたかどうか
 *
 *	@return int				評価点
 */
//-----------------------------------------------------------------------------
static int CheckGrading(PUSH_AI_CONT_PTR ptr, const RING_PRM *inRingPrm, const int inCellIdx,
		const AI_POKE_INFO *inInfo, const int inX,  const int inY, BOOL *outIsCenter)
{
	int i;
	int grading = 0;
	//セルインデックスが取得失敗してるなら、処理中断
	if (inCellIdx < 0){
		return 0;
	}

	*outIsCenter = FALSE;

	//スタミナある？
	if ( (*inInfo->Stamina != 0)&&(*inInfo->State == POKE_ALIVE) ){		//スタミナある
		//移動先座標がリング上
		if ( CheckRingIn(inRingPrm, inX*FX32_ONE, inY*FX32_ONE) ){
			grading+=5;
		}
		//セル内に円の中心がある
		if ( CheckCircleCenter(ptr, inCellIdx) ){
			grading+=4;
			*outIsCenter = TRUE;
		}
/**
		//20%で評価+1
		if (gf_rand()%100 < 20){
			grading++;
		}
*/
		//セル内に自分以外のポケがいる
		grading += CalcGradingPower(ptr, inInfo, inCellIdx);
	}else{		//スタミナない・ゾンビ状態
		int rc;
		//移動先座標がリング上
		if ( CheckRingIn(inRingPrm, inX*FX32_ONE, inY*FX32_ONE) ){
			grading+=5;
		}
		//セル内に円の中心がある
		if ( CheckCircleCenter(ptr, inCellIdx) ){
			grading+=4;
			*outIsCenter = TRUE;
		}
		rc = CalcGradingPower(ptr, inInfo, inCellIdx);
		//セル内にポケモンがいない(CalcGradingPowerで減算が行われてない)
		if (rc == 0){
			grading+=2;
		}
/**
		//20%で評価+1
		if (gf_rand()%100 < 20){
			grading++;
		}
*/
		//セル内に自分がいる
		if (inInfo->CellIdx == inCellIdx){
			grading+=2;
		}
		//セル内にポケモンがいる(CalcGradingPowerで減算された)
		if (  rc!=0 ){
			grading = 0;
		}
		//自分が円上にいる
		if ( CheckRingIn(inRingPrm, inInfo->Pos->x, inInfo->Pos->y) ){
			grading+=4;
		}
	}
	return grading;
}

//----------------------------------------------------------------------------
/**
 *	@brief	自分のポケのいるセルにポケモンがいる場合の評価値計算
 *
 *	@param	ptr		管理ポインタ
 *	@param	*inInfo　対象ポケ
 *	@param	inCellIdx	対象セルインデックス
 *
 *	@return	int	評価点
 */
//-----------------------------------------------------------------------------
static int CalcGradingPower(PUSH_AI_CONT_PTR ptr, const AI_POKE_INFO *inInfo, const int inCellIdx)
{
	int grading;
	int i;

	//セルインデックス取得できてないなら処理終わり
	if (inInfo->CellIdx == -1){
		return 0;
	}

	grading = 0;
	for (i=0;i<POKE_NUM_MAX;i++){
		AI_POKE_INFO *info;
		info = &ptr->Info[i];
		if (i != inInfo->PokeIndex){	//自分は対象外
			//同じセルにいるか？
			if ( inCellIdx != info->CellIdx ){
				//違うセル
				continue;
			}

			if ( inInfo->Power < info->Power ){
				//同チーム判定
				if( inInfo->PokeIndex/ENTRY_MAX == info->PokeIndex/ENTRY_MAX ){
					//同じチーム
					grading--;
				}else{
					//別チーム
					grading-=2;
				};
			}else{
				grading--;
			}
		}
	}
	return grading;
}

//----------------------------------------------------------------------------
/**
 *	@brief	指定座標がリング上にあるか？
 *
 *	@param	*inRingPrm	リングパラメータ
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *
 *	@return	BOOL		TRUEならリング上
 */
//-----------------------------------------------------------------------------
static BOOL CheckRingIn(const RING_PRM *inRingPrm, const fx32 inX, const fx32 inY)
{
	int i;
	fx32 len;
	VecFx32 vec;

	vec.x = inX;
	vec.y = inY;
	vec.z = 0;


	for(i=0;i<RING_MAX;i++){
		VecFx32 cvec;
		if (inRingPrm[i].Range <= 0){
			break;
		}
		cvec.x = inRingPrm[i].X * FX32_ONE;
		cvec.y = inRingPrm[i].Y * FX32_ONE;
		cvec.z = 0;
		//方向ベクトル取得
		VEC_Subtract( &vec, &cvec, &cvec );
		//方向ベクトルの長さを取得
		len = VEC_Mag(&cvec);
		if (len < inRingPrm[i].Range*FX32_ONE){
			return TRUE;
		}
	}
	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	円の中心が指定セルにあるか？
 *
 *	@param	ptr			管理ポインタ
 *	@param	inCellIdx	セルインデックス
 *
 *	@return	BOOL		TRUEなら中心がある
 */
//-----------------------------------------------------------------------------
static BOOL CheckCircleCenter(PUSH_AI_CONT_PTR ptr, const u8 inCellIdx)
{
	int i;
	for(i=0;i<RING_MAX;i++){
		if (ptr->RingCenterCell[i] == -1){
			break;
		}
		if (ptr->RingCenterCell[i] == inCellIdx){
			return TRUE;
		}
	}
	return FALSE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	円の中心座標を取得
 *
 *	@param	ptr			管理ポインタ
 *	@param	inCellIdx	セルインデックス
 *	@param	outX		円中心X座標
 *	@param	outY		円中心Y座標
 *
 *	@return	none
 */
//-----------------------------------------------------------------------------
static void GetCircleCenter(PUSH_AI_CONT_PTR ptr, const RING_PRM *inRingPrm, const u8 inCellIdx,
							int *outX, int *outY)
{
	int i;
	for(i=0;i<RING_MAX;i++){
		if (ptr->RingCenterCell[i] == inCellIdx){
			*outX = inRingPrm[i].X;
			*outY = inRingPrm[i].Y;
			break;
		}
	}
}

//----------------------------------------------------------------------------
/**
 *	@brief	今の現在座標のセル座標を取得
 *
 *	@param	inX			指定X座標
 *	@param	inY			指定Y座標
 *	@param	*outCX		リングセルX座標
 *	@param	*outCY		リングセルY座標
 *
 *	@return	BOOL		TRUEならセル座標取得成功
 */
//-----------------------------------------------------------------------------
static BOOL GetCell(const u16 inX, const u16 inY, s16 *outCX, s16 *outCY)
{

	*outCX = (inX - CELL_OFS) / CELL_SIZE;
	*outCY = (inY - CELL_OFS) / CELL_SIZE;

	if ((*outCX < 0) || (*outCY < 0)){
		return FALSE;
	}
	return TRUE;
}

//----------------------------------------------------------------------------
/**
 *	@brief	データソート
 *
 *	@param	inSortNum	要素数
 *	@param	*inInfo		ソート前データ
 *	@param	*outInfo	ソート後データ
 *
 *	@return	count		一番大きい値を持った要素数の個数
 */
//-----------------------------------------------------------------------------
static u8 SortInfo(const u8 inSortNum, CELL_INFO **inInfo, CELL_INFO **outInfo)
{
	u8 count;
	u8 i,j;
	INFO_NODE node[1+ALL_CELL_NUM];

	//準備
	node[0].Prev = &node[1];
	node[0].Next = &node[1];
	node[0].Info = NULL;

	count = 0;

	for(i=0;i<inSortNum;i++){
		INFO_NODE *now;
		INFO_NODE *target;
		//ノード取得
		now = &node[i+1];
		//値セット
		now->Info = inInfo[i];
		now->Next = NULL;
		now->Prev = NULL;

		target = &node[0];
		for(j=0;j<i;j++){
			target = target->Next;
			//挿入
			if (target->Info->Grading < now->Info->Grading){	//値が大きい
				//リストの前に追加
				target->Prev->Next = now;
				now->Prev = target->Prev;
				now->Next = target;
				target->Prev = now;
				break;
			}
		}
		if (j==i){
			//リスト最後に追加
			target->Next = now;
			now->Prev = target;
		}
	}
	//ソート終了したら、バッファにデータを格納
	{
		INFO_NODE *now;
		u8 check;
		int max_val;
		//最大値の取得
		max_val = node[0].Next->Info->Grading;
		now = &node[0];
		for(i=0;i<inSortNum;i++){
			now = now->Next;
			outInfo[i] = now->Info;
			//一番高い値がいくつあるかカウント
			if (max_val == outInfo[i]->Grading){
				count++;
			}
		}
	}

	GF_ASSERT(count > 0);

	return count;
}